/**    
 * 文件名：AmortizationService.java</br>
 *    
 * 版本信息：v1.0</br>
 * 日期：2017年11月8日</br>
 * © 2005-2017 雷技信息科技（上海）有限公司版权所有</br>
 *
 */
package com.logic.landseaserver.models.amortization.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.logic.common.security.SecurityUtils;
import com.logic.common.util.QueryUtil;
import com.logic.landseaserver.common.config.FileConfiguration;
import com.logic.landseaserver.common.enums.BillTypeEnum;
import com.logic.landseaserver.common.enums.ContractStatusCodeEnum;
import com.logic.landseaserver.common.exception.LandseaException;
import com.logic.landseaserver.common.exception.ResultCodes.BillResultCode;
import com.logic.landseaserver.common.util.Arith;
import com.logic.landseaserver.common.util.DateUtil;
import com.logic.landseaserver.common.util.StringTools;
import com.logic.landseaserver.models.amortization.AmortizationCalculate;
import com.logic.landseaserver.models.amortization.bean.AmortizationBillDetail;
import com.logic.landseaserver.models.amortization.bean.AmortizationRecord;
import com.logic.landseaserver.models.amortization.bean.ContractBillMonthDetail;
import com.logic.landseaserver.models.refunddiary.service.RefundDiaryService;
import com.logic.landseaserver.persistence.read.BillDetailReadMapper;
import com.logic.landseaserver.persistence.read.ContractReadMapper;

/**
 *
 * 项目名称：system-server</br>
 * 类名称：AmortizationService</br>
 * 类描述：</br>
 * 创建人：Aaron</br>
 * 创建时间：2017年11月8日 下午3:37:37</br>amortizationExcel
 * 
 * @version 1.0
 *
 */
@Service
public class AmortizationService
{
    private static final Logger LOGGER = LoggerFactory.getLogger(AmortizationService.class);
    
    @Autowired
    private ContractReadMapper contractReadMapper;
    
    @Autowired
    private BillDetailReadMapper billDetailReadMapper;
    
    @Autowired
    private FileConfiguration fileConfiguration;
    
    /**
     * 
     * [简要描述]：分页查询</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-11-08)</br>
     *
     * @param queryUtil
     * @return
     *
     */
    public List<AmortizationRecord> pageList(QueryUtil queryUtil)
        throws LandseaException
    {
        try
        {
            // 获取请求中的摊销日期
            Map<String, Object> queryMap = queryUtil.getParams();
            String startTimeStr = (String)queryMap.get("startTime");
            String endTimeStr = (String)queryMap.get("endTime");
            
            // 精确到天
            Date startTime = DateUtil.convertDate(startTimeStr);
            Date endTime = DateUtil.convertDate(endTimeStr);
            if (null == startTime || null == endTime)
            {
                throw LandseaException.createException(BillResultCode.E004500019);
            }
            if (startTime.getTime() > endTime.getTime())
            {
                throw LandseaException.createException(BillResultCode.E004500020);
            }
            
            // 过滤掉删除态以及草稿的合同
            List<AmortizationRecord> resultList = contractReadMapper.amortizationPageList(queryUtil);
            
            if (CollectionUtils.isEmpty(resultList))
            {
                return resultList;
            }
            
            // 获取contractId的参数
            List<Integer> contractIdList = new ArrayList<Integer>();
            Map<Integer, Double> contractRentMap = new HashMap<Integer, Double>();
            Map<Integer, Double> contractServiceMap = new HashMap<Integer, Double>();
            for (AmortizationRecord ar : resultList)
            {
                contractIdList.add(ar.getContractId());
                contractRentMap.put(ar.getContractId(), ar.getMonthRentMoney());
                contractServiceMap.put(ar.getContractId(), ar.getMonthServiceMoney());
            }
            String contractIdStr = StringTools.packageAndInSqlbyListInt(" and lb.contract_id ", contractIdList);
            // 查询合同的所有月份的房租、服务费账单
            Map<String, Object> paramMap = new HashMap<String, Object>();
            paramMap.put("serviceFee", BillTypeEnum.CTBI9.getCode());
            paramMap.put("rentFeek", BillTypeEnum.CTBI8.getCode());
            paramMap.put("contractIds", contractIdStr);
            List<AmortizationBillDetail> contractBillDetailList = billDetailReadMapper.query4Amortization(paramMap);
            
            // 处理房租和服务费按每天计算摊销点
            Map<Integer, Map<String, Double>> resultRentMap =
                doProcess(contractBillDetailList, BillTypeEnum.CTBI8.getCode(), contractRentMap);
            Map<Integer, Map<String, Double>> resultServiceMap =
                doProcess(contractBillDetailList, BillTypeEnum.CTBI9.getCode(), contractServiceMap);
            
            // 计算合同的摊销时间
            for (AmortizationRecord ar : resultList)
            {
                // 获取最终的摊销时间
                getAmortizationTimes(ar, startTime, endTime);
                Map<String, Double> dayRentMoneyMap  = resultRentMap.get(ar.getContractId());
                if (null != dayRentMoneyMap)
                {
                    // 计算摊销房租
                    double rentMoney = AmortizationCalculate.getResultMoney(startTime, endTime, dayRentMoneyMap);
                    ar.setRentMoney(rentMoney);
                }
                else
                {
                    ar.setRentMoney(0.00d);
                }
                Map<String, Double> dayServiceMoneyMap  = resultServiceMap.get(ar.getContractId());
                if (null != dayRentMoneyMap)
                {
                    // 计算摊销服务费
                    double serviceMoney = AmortizationCalculate.getResultMoney(startTime, endTime, dayServiceMoneyMap);
                    ar.setServiceMoney(serviceMoney);
                }
                else
                {
                    ar.setServiceMoney(0.00d);
                }
            }
            
            return resultList;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("查询摊销列表失败.", e);
            throw LandseaException.createException(BillResultCode.E004500018, e);
        }
    }
    
    // 获取真实摊销时间段
    private void getAmortizationTimes(AmortizationRecord record, Date startTime, Date endTime)
    {
        // 合同的有效开始时间(格式化为精确到天)
        Date contractStartTime = DateUtil.dateConvertDate(record.getInDate());
        
        // 获取真实的合同结束日期
        Date contractEndTime = null;
        // 先判断合同状态
        if (ContractStatusCodeEnum.SIGN.getCode().equals(record.getContractStatus()))
        {
            // 如果是签约中，有效截止日期就获取当前时间
            contractEndTime = new Date();
        }
        else if (ContractStatusCodeEnum.END.getCode().equals(record.getContractStatus())
            && null != record.getOffrentDate())
        {
            // 退租时，获取实际的退租时间为合同结束日期
            contractEndTime = record.getOffrentDate();
        }
        else if (ContractStatusCodeEnum.END.getCode().equals(record.getContractStatus())
            && null == record.getOffrentDate())
        {
            // 若合同结束了，但没有退租日期，获取合同的结束日期
            contractEndTime = record.getOutDate();
        }
        
        // 格式化为精确到天
        contractEndTime = DateUtil.dateConvertDate(contractEndTime);
        
        /*
         * 以下为具体摊销时间判断，按照摊销选择时间段和以上的开始、结束时间关系判断 CS：合同真实开始日期 CE：合同真实结束日期 TS：摊销开始日期 TE：摊销结束日期
         */
        long cs = contractStartTime.getTime();
        long ce = contractEndTime.getTime();
        long ts = startTime.getTime();
        long te = endTime.getTime();
        
        // 1. 示意图：TS TE CS CE 摊销日期范围在合同开始之前,摊销房租和服务为0
        if (te < cs)
        {
            return;
        }
        
        // 2. 示意图：TS CS TE CE
        if (ts <= cs && te >= cs && te < ce)
        {
            startTime = contractStartTime;
            return;
        }
        
        // CS TS TE CE 
        if (ts >= cs && te <= ce)
        {
            return;
        }
        
        // CS TS CE TE 
        if (ts >= cs && ts <= ce && te >= ce)
        {
            endTime = contractEndTime;
            return;
        }
        
        // CS CE TS TE 
        if (ts >= ce && te >= ce)
        {
            return;
        }
        
        // TS CS CE TE 
        if (ts <= cs && te >= ce)
        {
            startTime = contractStartTime;
            endTime = contractEndTime;
            return;
        }
    }
    
    private Map<Integer, Map<String, Double>> doProcess(List<AmortizationBillDetail> billDetailList,
        String billDetailType, Map<Integer, Double> contractMonthMoneyMap)
            throws LandseaException
    {
        try
        {
            // 最终的返回集合，key为合同id，value是该合同有效入住时间段内每一天的实际收入
            Map<Integer, Map<String, Double>> resultMap = new HashMap<Integer, Map<String, Double>>();
            
            if (CollectionUtils.isEmpty(billDetailList))
            {
                return resultMap;
            }
            
            Map<String, ContractBillMonthDetail> contractBillMonthMap = new HashMap<String, ContractBillMonthDetail>();
            
            // key： contractId_billMonth
            String key = null;
            // 按合同+账单月分组
            ContractBillMonthDetail contractBillMonthDetail = null;
            
            // 授权码优惠
            Double couponMoney = null;
            
            for (AmortizationBillDetail abd : billDetailList)
            {
                if (!billDetailType.equals(abd.getBillDetailType()))
                {
                    continue;
                }
                
                key = abd.getContractId() + "_" + abd.getBillMonth();
                contractBillMonthDetail = contractBillMonthMap.get(key);
                if (null == contractBillMonthDetail)
                {
                    List<AmortizationBillDetail> list = new ArrayList<AmortizationBillDetail>();
                    contractBillMonthDetail = new ContractBillMonthDetail();
                    contractBillMonthDetail.setList(list);
                    contractBillMonthMap.put(key, contractBillMonthDetail);
                }
                
                contractBillMonthDetail.setOffRent(abd.getIsOffRent());
                contractBillMonthDetail.setContractId(abd.getContractId());
                
                if (abd.getIsHaveCoupon())
                {
                    // 处理授权码
                    if (null == contractBillMonthDetail.getCouponMoney())
                    {
                        couponMoney = abd.getMoney();
                    }
                    else
                    {
                        couponMoney = Arith.add(contractBillMonthDetail.getCouponMoney(), abd.getMoney());
                    }
                    contractBillMonthDetail.setCouponMoney(couponMoney);
                    contractBillMonthDetail.setHaveCoupon(abd.getIsHaveCoupon());
                }
                else
                {
                    contractBillMonthDetail.getList().add(abd);
                }
            }
            
            // 对每一个分组处理
            Set<Entry<String, ContractBillMonthDetail>> set = contractBillMonthMap.entrySet();
            List<AmortizationBillDetail> abdList = null;
            Map<String, Double> dayMoneyMap = null;
            for (Entry<String, ContractBillMonthDetail> entry : set)
            {
                ContractBillMonthDetail cbmd = entry.getValue();
                
                Map<String, Double> contractDayMoneyMap = resultMap.get(cbmd.getContractId());
                if (null == contractDayMoneyMap)
                {
                    contractDayMoneyMap = new HashMap<String, Double>();
                    resultMap.put(cbmd.getContractId(), contractDayMoneyMap);
                }
                
                abdList = cbmd.getList();
                // 如果是退租的话，要特殊处理
                if (cbmd.isOffRent())
                {
                    // 退租有两种情况，当前月账单未支付完成、支付完成
                    if (abdList.size() == 1)
                    {
                        // 如果只有一条记录，说明账单已合并
                        AmortizationBillDetail amortizationBillDetail = abdList.get(0);
                        dayMoneyMap = AmortizationCalculate.calculate(amortizationBillDetail.getBeginDate(),
                            amortizationBillDetail.getEndDate(),
                            contractMonthMoneyMap.get(cbmd.getContractId()),
                            amortizationBillDetail.getMoney(), cbmd.getCouponMoney());
                    }
                    else
                    {
                        // 如果退租账单detail有两条记录,开始日期取第一条记录的beginDate期，结束日期取第二条记录的beginDate
                        dayMoneyMap = AmortizationCalculate.calculate(abdList.get(0).getBeginDate(),
                            abdList.get(1).getBeginDate(),
                            contractMonthMoneyMap.get(cbmd.getContractId()),
                            Arith.add(abdList.get(0).getMoney(), abdList.get(1).getMoney()), cbmd.getCouponMoney());
                    }
                }
                else
                {
                    // 非退租,肯定只有一条记录的
                    AmortizationBillDetail amortizationBillDetail = abdList.get(0);
                    dayMoneyMap = AmortizationCalculate.calculate(amortizationBillDetail.getBeginDate(),
                        amortizationBillDetail.getEndDate(),
                        contractMonthMoneyMap.get(cbmd.getContractId()),
                        amortizationBillDetail.getMoney(), cbmd.getCouponMoney());
                }
                
                contractDayMoneyMap.putAll(dayMoneyMap);
            }
            
            return resultMap;
        }
        catch (LandseaException e)
        {
            throw e;
        }
        catch (Exception e)
        {
            LOGGER.error("doProcess|计算摊销失败.", e);
            throw LandseaException.createException(BillResultCode.E004500022, e);
        }
    }
    
    /**
     * 
     * [简要描述]：导出摊销查询列表</br>
     * [详细描述]：</br>
     * [作者]：Aaron(2017-11-13)</br>
     *
     * @param queryUtil
     * @return
     * @throws LandseaException
     *
     */
    public String amortizationExcel(QueryUtil queryUtil) throws LandseaException
    {
        String url = fileConfiguration.getRemote();
        String excelpath = fileConfiguration.getExcelPath();
        
        InputStream ins = null;
        Workbook workbook = null;
        try
        {
            ins = RefundDiaryService.class.getClassLoader().getResourceAsStream("租金收入确认表.xlsx");
            workbook = WorkbookFactory.create(ins);
        }
        catch (Exception e)
        {
            LOGGER.error("amortizationExcel|读取Excel模板失败.", e);
            throw LandseaException.createException(BillResultCode.E004500023, e);
        }
        finally
        {
            try
            {
                if (null != ins)
                {
                    ins.close();
                }
            }
            catch (IOException e)
            {
                LOGGER.error("amortizationExcel|关闭读取流失败.", e);
                throw LandseaException.createException(BillResultCode.E004500023, e);
            }
        }
        
        Map<String, Object> queryMap = queryUtil.getParams();
        // 摊销开始时间
        String startTimeStr = (String)queryMap.get("startTime");
        // 摊销结束时间
        String endTimeStr = (String)queryMap.get("endTime");
        
        
        // 根据条件，查询所有
        List<AmortizationRecord> recordList = pageList(queryUtil);
        
        if (CollectionUtils.isEmpty(recordList))
        {
            recordList = new ArrayList<AmortizationRecord>();
        }
        
        Sheet sheet = workbook.getSheet("租金收入确认表");
        // 生成一个样式
        CellStyle style = workbook.createCellStyle();
        // 设置这些样式
        style.setAlignment(CellStyle.ALIGN_CENTER);
        // 生成一个字体
        Font font = workbook.createFont();
        font.setFontHeightInPoints((short)14);
        font.setBoldweight(Font.BOLDWEIGHT_BOLD);
        // 把字体应用到当前的样式
        style.setFont(font);
        
        // 修改表头的摊销日期(模板中在O1单元格)
        Row firstRow = sheet.getRow(0);
        Cell amortizationDateCell = firstRow.getCell(15);
        amortizationDateCell.setCellType(Cell.CELL_TYPE_STRING);
        amortizationDateCell.setCellValue("摊销日期：" + startTimeStr + "至" + endTimeStr);
        
        Row row;
        int startRow = 2;
        for (int i = 0; i < recordList.size(); i ++)
        {
            AmortizationRecord amortizationRecord = recordList.get(i);
            
            row = sheet.createRow(startRow + i);
            // 序号
            row.createCell(0).setCellValue(i + 1);
            // 公司
            row.createCell(1).setCellValue(StringTools.getStr(amortizationRecord.getAreaCodeZh()));
            // 门店
            row.createCell(2).setCellValue(StringTools.getStr(amortizationRecord.getProjectName()));
            // 合同编号
            row.createCell(3).setCellValue(StringTools.getStr(amortizationRecord.getContractNo()));
            // 房间号
            row.createCell(4).setCellValue(StringTools.getStr(amortizationRecord.getHouseNum()));
            // 客户姓名
            row.createCell(5).setCellValue(StringTools.getStr(amortizationRecord.getUsername()));
            // 付租方式
            row.createCell(6).setCellValue(StringTools.getStr(amortizationRecord.getPaymentModeZh()));
            // 最近一次付款周期
            row.createCell(7).setCellValue(StringTools.getStr(amortizationRecord.getLastPayCycle()));
            // 最近一次付款时间
            row.createCell(8).setCellValue(StringTools.getStr(DateUtil.convertDate(amortizationRecord.getLastPayTime())));
            // 合同开始时间
            row.createCell(9).setCellValue(StringTools.getStr(DateUtil.convertDate(amortizationRecord.getInDate())));
            // 合同结束时间
            row.createCell(10).setCellValue(StringTools.getStr(DateUtil.convertDate(amortizationRecord.getOutDate())));
            
            
            Date offrentDate = amortizationRecord.getOffrentDate();
            String offrentDateStr = StringUtils.EMPTY;
            if (null == offrentDate && ContractStatusCodeEnum.END.getCode().equals(amortizationRecord.getContractStatus()))
            {
                // 如果实际退租日期为空，则取合同的结束日期
                offrentDateStr = StringTools.getStr(DateUtil.convertDate(amortizationRecord.getOutDate()));
            }
            else
            {
                offrentDateStr = StringTools.getStr(DateUtil.convertDate(offrentDate));
            }
            
            // 实际退租日
            row.createCell(11).setCellValue(offrentDateStr);
            // 月租金
            row.createCell(12).setCellValue(StringTools.getStr(String.valueOf(amortizationRecord.getMonthRentMoney())));
            // 房屋押金
            row.createCell(13).setCellValue(StringTools.getStr(String.valueOf(amortizationRecord.getDepositMoney())));
            // 服务费
            row.createCell(14).setCellValue(StringTools.getStr(String.valueOf(amortizationRecord.getMonthServiceMoney())));
            // 摊销租金
            row.createCell(15).setCellValue(String.valueOf(amortizationRecord.getRentMoney()));
            // 摊销服务费
            row.createCell(16).setCellValue(String.valueOf(amortizationRecord.getServiceMoney()));
        }
        
        // 下面是生成excel文件
        OutputStream fos = null;
        try
        {
            Integer userId = SecurityUtils.getCurrentLogin().getUserId();
            String filepath = fileConfiguration.getLocal() + File.separator + excelpath;
            File file = new File(filepath);
            // 如果文件夹不存在则创建
            if (!file.exists() && !file.isDirectory())
            {
                boolean mkdirResult  = file.mkdir();
                if (!mkdirResult)
                {
                    LOGGER.error("amortizationExcel|创建临时文件夹失败.filepath=" + filepath);
                    throw LandseaException.createException(BillResultCode.E004500023);
                }
            }
            
            // 删除userId开头的excel
            String fileExcelPre = "租金收入确认表_" + userId + "_";
            String[] files = file.list(new FilenameFilter()
            {
                @Override
                public boolean accept(File dir, String name)
                {
                    return name.startsWith(fileExcelPre) && name.endsWith(".xlsx");
                }
            });
            if (null != files)
            {
                // 获取7天前的时间戳
                Calendar calendar = Calendar.getInstance();
                calendar.add(Calendar.DAY_OF_YEAR, -7);
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                long currentDateLong = calendar.getTimeInMillis();
                
                // 删除修改时间一周之前的文件
                for (String f : files)
                {
                    File fileDelete = new File(filepath + File.separator + f);
                    if (currentDateLong >= fileDelete.lastModified())
                    {
                        fileDelete.delete();
                    }
                }
            }
            
            Date nowTime = new Date();
            String time = DateUtil.convertWeBankTime(nowTime);
            
            String fileName = fileExcelPre + time +".xlsx";
            fos = new FileOutputStream(filepath + File.separator + fileName);
            workbook.write(fos);
            
            fos.flush();
            
            StringBuffer returnUrl = new StringBuffer();
            returnUrl.append(url);
            returnUrl.append(File.separator);
            returnUrl.append(excelpath);
            returnUrl.append(File.separator);
            returnUrl.append(fileName);
            return returnUrl.toString();
        }
        catch (Exception e)
        {
            LOGGER.error("amortizationExcel|生成摊销excel失败.", e);
            throw LandseaException.createException(BillResultCode.E004500023);
        }
        finally
        {
            try
            {
                if (null != fos)
                {
                    fos.close();
                }
            }
            catch (IOException e)
            {
                LOGGER.error("amortizationExcel|生成摊销excel关闭流失败.", e);
                throw LandseaException.createException(BillResultCode.E004500023);
            }
        }
    }
}
